package at.horario.web.pages;

import at.horario.model.*;
import at.horario.service.ClasseManager;
import at.horario.service.GenericManager;
import at.horario.service.HorarioManager;
import static org.apache.commons.lang.builder.ToStringBuilder.reflectionToString;
import static org.apache.commons.lang.builder.ToStringStyle.MULTI_LINE_STYLE;
import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.*;
import org.apache.wicket.markup.html.panel.ComponentFeedbackPanel;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.*;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.collections.MicroMap;
import static org.apache.wicket.util.string.interpolator.MapVariableInterpolator.interpolate;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@AuthorizeInstantiation({"user", "admin"})

public class ProcessarForm extends PageTemplate {
    @SpringBean(name = "horarioManager")
    private HorarioManager horarioManager;
    @SpringBean(name = "cicloManager")
    private GenericManager<Ciclo, Integer> cicloManager;
    @SpringBean(name = "classeManager")
    private ClasseManager classeManager;

    private final Page backPage;


    Map<String, Object> classeGrupo;

    /**
     * Constructor user to create a new user
     *
     * @param backPage page to navigate to after this page completes its work
     */
    public ProcessarForm(Page backPage) {
        this(backPage, new Horario());
    }

    public ProcessarForm() {
        this(new HorarioList(), new Horario());
    }

    /**
     * Constructor used to edit an horario
     *
     * @param backPage page to navigate to after this page completes its work
     * @param horario  horario to edit
     */
    public ProcessarForm(final Page backPage, Horario horario) {
        this.backPage = backPage;

        classeGrupo = new HashMap<String, Object>();

        // Create and add the form
        EditForm form = new EditForm("horario-form", horario) {
            protected void onSave(Horario horario, Map<String, Object> classeGrupo) {
                onSaveHorario(horario, classeGrupo);
            }

            protected void onCancel() {
                onCancelEditing();
            }

            protected void onDelete(Horario user) {
                onDeleteUser(user);
            }
        };
        add(form);
    }

    /**
     * Listener method for save action
     *
     * @param horario horario bean
     */
    protected void onSaveHorario(Horario horario, Map<String, Object> classeGrupo) {
        horarioManager.save(horario, classeGrupo);

        String message = interpolate(getLocalizer().getString("horario.saved", this),
                new MicroMap("name", horario.getId()));
        getSession().info(message);
        backPage.get("feedback").setVisible(true);

        setRedirect(true);
        setResponsePage(backPage);
    }

    /**
     * Listener method for delete action
     *
     * @param user user bean
     */
    protected void onDeleteUser(Horario user) {
//        userManager.remove(user.getId());
//
//        String message = MapVariableInterpolator.interpolate(getLocalizer().getString("user.deleted", this),
//                new MicroMap("name", user.getFullName()));
//        getSession().info(message);
//
//        backPage.get("feedback").setVisible(true);
//        setRedirect(true);
//        setResponsePage(backPage);
    }

    /**
     * Lister method for cancel action
     */
    private void onCancelEditing() {
        setResponsePage(backPage);
    }


    private abstract class EditForm extends BaseForm {

        private RepeatingView[] cicloViews;


        /**
         * Convenience method that adds and prepares a form component
         *
         * @param fc    form component
         * @param label IModel containing the string used in ${label} variable of
         *              validation messages
         */
        protected void add(FormComponent fc, IModel label) {
            // Add the component to the form
            super.add(fc);
            // Set its label model
            fc.setLabel(label);
            // Add feedback panel that will be used to display component errors
            add(new ComponentFeedbackPanel(fc.getId() + "-feedback", fc));
        }

        /**
         * Constructor
         *
         * @param id      component id
         * @param horario User object that will be used as a form bean
         */
        public EditForm(String id, Horario horario) {
            /*
             * We wrap the horario bean with a CompoundPropertyModel, this allows
             * us to easily connect form components to the bean properties
             * (component id is used as the property expression)
             */

            super(id, new CompoundPropertyModel(horario));

            List<Ciclo> cicloList = getCicloList();

            final DropDownChoice cicloCombo = new DropDownChoice("ciclo", cicloList);
            cicloCombo.setRequired(true);

            DropDownChoice turnoCombo = new DropDownChoice("turno", Arrays.asList(Turno.values()));
            turnoCombo.setRequired(true);


            TextField descricaoTextField = new TextField("descricao");
            descricaoTextField.setRequired(true);

            add(cicloCombo, new ResourceModel("horario.ciclo"));
            add(turnoCombo, new ResourceModel("horario.turno"));
            add(descricaoTextField, new ResourceModel("horario.descricao"));


            cicloViews = new RepeatingView[cicloList.size()];
            for (int i = 0; i < cicloList.size(); i++) {
                Ciclo ciclo = cicloList.get(i);
                RepeatingView cicloView;
                cicloViews[i] = cicloView = new RepeatingView("ciclo" + ciclo.getCicloId());
                add(cicloView);
                cicloView.setVisible(false);
                //cicloView.setMarkupId("cicloView"+ciclo.getCicloId());
                cicloView.setOutputMarkupId(true);


                for (Classe classe : ciclo.getClasses()) {
                    for (Grupo grupo : classe.getGrupos()) {
                        WebMarkupContainer row = new WebMarkupContainer(cicloView.newChildId());
                        cicloView.add(row);

                        String classegrupo = "classe" + classe.getClasseId() + grupo.getGrupoNome();
                        IModel labelModel = new StringResourceModel("horario." + classegrupo, this, null);
                        // Use Wicket's i18n to name the field.
                        row.add(new Label("name", labelModel));

                        // Create a model to bind our editor component to the bean.
                        IModel classe8Global = new ClasseGrupoModel(classegrupo);

                        Component editor = new StringEditor("editor", classe8Global, labelModel, true, Integer.class);

                        row.add(editor);
                    }
                }
            }

            cicloCombo.add(new AjaxFormComponentUpdatingBehavior("onchange") {
                protected void onUpdate(AjaxRequestTarget target) {
                    Ciclo ciclo = (Ciclo) cicloCombo.getModelObject();

                    log.info(reflectionToString(target, MULTI_LINE_STYLE));
                    for (int i = 0; i < cicloViews.length; i++) {
                        RepeatingView cicloView = cicloViews[i];

                        if (ciclo.getCicloId() == (i + 1))
                            cicloView.setVisible(true);
                        else
                            cicloView.setVisible(false);
                    }
                    target.addComponent(EditForm.this);
                }
            });


            add(new Button("save", new Model("Save")) {
                public void onSubmit() {
                    onSave((Horario) getForm().getModelObject(), classeGrupo);
                }
            });

            Button delete = new Button("delete", new Model("Delete")) {
                public void onSubmit() {
                    onDelete((Horario) getForm().getModelObject());
                }
            };

//            if (horario.getId() == null) {
//                delete.setVisible(false);
//                delete.setEnabled(false);
//            }
            add(delete);

            /*
             * Notice the setDefaultFormProcessing(false) call at the end. This
             * tells wicket that when this button is pressed it should not
             * perform any form processing (ie bind request values to the bean).
             */
            add(new Button("cancel", new Model("Cancel")) {
                public void onSubmit() {
                    onCancel();
                }
            }.setDefaultFormProcessing(false));

        }

        /**
         * Callback for cancel button
         */
        protected abstract void onCancel();

        /**
         * Callback for delete button
         *
         * @param user user bean
         */
        protected abstract void onDelete(Horario user);

        /**
         * Callback for save button
         *
         * @param user user bean
         */
        protected abstract void onSave(Horario user, Map<String, Object> classeGrupo);
    }

    private List getCicloList() {
        return cicloManager.getAll();
    }

    private class ClasseGrupoModel implements IModel {
        String name;

        private ClasseGrupoModel(String name) {
            this.name = name;
        }

        public Object getObject() {
            return classeGrupo.get(name);
        }

        public void setObject(Object object) {
            classeGrupo.put(name, object);
        }

        public void detach() {

        }
    }

    private static class StringEditor extends Fragment {
        public StringEditor(String id, IModel model, IModel labelModel, boolean required, Class type) {
            super(id, "stringEditor");
            FormComponent fc = new TextField("edit", model).setLabel(labelModel).setRequired(required);
            fc.setType(type);
            add(fc);
            add(new ComponentFeedbackPanel("feedback", fc));
        }
    }

    private static class BooleanEditor extends Fragment {
        public BooleanEditor(String id, IModel model, IModel labelModel) {
            super(id, "booleanEditor");
            add(new CheckBox("edit", model).setLabel(labelModel));
        }
    }

    private static class EnumEditor extends Fragment {
        public EnumEditor(String id, IModel model, IModel labelModel, IModel choices) {
            super(id, "enumEditor");
            add(new DropDownChoice("edit", model, choices).setLabel(labelModel));
        }
    }

}